/*
 *  Copyright 2004 Blandware (http://www.blandware.com)
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.blandware.atleap.webapp.taglib.core.util;

import com.blandware.atleap.common.util.StringUtil;
import com.blandware.atleap.webapp.util.core.WebappConstants;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * <p>Contains some helper methods usable by tags, which produce some JavaScript code</p>
 * <p><a href="JavaScriptUtil.java.html"><i>View Source</i></a></p>
 *
 * @author Sergey Zubtsovskiy <a href="mailto:sergey.zubtsovskiy@blandware.com">&lt;sergey.zubtsovskiy@blandware.com&gt;</a>
 * @version $Revision: 1.12 $ $Date: 2007/12/08 17:51:56 $
 */
public class JavaScriptUtil {

	/**
	 * JavaScript event handler that is executed when this element receives a mouse click.
	 */
	public static final String EVENT_TYPE_ONCLICK = "onclick";

	/**
	 * JavaScript event handler that is executed when this element receives a mouse double click.
	 */
	public static final String EVENT_TYPE_ONDBLCLICK = "ondblick";

	/**
	 * JavaScript event handler that is executed when this element receives a key up event.
	 */
	public static final String EVENT_TYPE_ONKEYUP = "onkeyup";

	/**
	 * JavaScript event handler that is executed when this element receives a key down event.
	 */
	public static final String EVENT_TYPE_ONKEYDOWN = "onkeydown";

	/**
	 * JavaScript event handler that is executed when this element receives a key press event.
	 */
	public static final String EVENT_TYPE_ONKEYPRESS = "onkeypress";

	/**
	 * JavaScript event handler that is executed when this element receives a mouse move event.
	 */
	public static final String EVENT_TYPE_ONMOUSEMOVE = "onmousemove";

	/**
	 * JavaScript event handler that is executed when this element receives a mouse up event.
	 */
	public static final String EVENT_TYPE_ONMOUSEUP = "onmouseup";

	/**
	 * JavaScript event handler that is executed when this element receives a mouse down event.
	 */
	public static final String EVENT_TYPE_ONMOUSEDOWN = "onmousedown";

	/**
	 * JavaScript event handler that is executed when this element receives a mouse over event.
	 */
	public static final String EVENT_TYPE_ONMOUSEOVER = "onmouseover";

	/**
	 * JavaScript event handler that is executed when this element receives a mouse out event.
	 */
	public static final String EVENT_TYPE_ONMOUSEOUT = "onmouseout";

	/**
	 * JavaScript event handler that is executed when this element receives input focus.
	 */
	public static final String EVENT_TYPE_ONFOCUS = "onfocus";

	/**
	 * JavaScript event handler that is executed when this element loses input focus.
	 */
	public static final String EVENT_TYPE_ONBLUR = "onblur";

	/**
	 * Creates an HTML <code>DIV</code> element which behaves like hyperlink. Usable, for example, in context menus
     * 
	 * @param url Location of link
	 * @param body Body of link
	 * @return String representation of generated HTML element
	 */
	public static String createLinkAsLayer(String url, String body,
                                           String styleClass, String highlightedStyleClass) {
		HTMLElement div = new HTMLElement("div");
        div.putAttribute("class", WebappConstants.CONTEXT_MENU_ITEM_STYLE_CLASS);
        div.putAttribute("onmouseover", "activateContextMenuItem(this, '" + highlightedStyleClass + "');");
        div.putAttribute("onmouseout", "deactivateContextMenuItem(this, '" + styleClass + "');");
		div.putAttribute("url", url);
		div.putAttribute(EVENT_TYPE_ONCLICK, "onMenuItemClick(this);");
		div.setBodyContent(body);
		return div.toString();
	}

	/**
	 * Private constructor, because this class has static methods only
	 */
	private JavaScriptUtil() {
	}

	/**
	 * Represents an HTML element. For example, <code>A</code> or <code>DIV</code>.
	 *
	 * @author Sergey Zubtsovskiy <a href="mailto:sergey.zubtsovskiy@blandware.com">&lt;sergey.zubtsovskiy@blandware.com&gt;</a>
	 */
	public static class HTMLElement implements Serializable {

		/**
		 * Name of HTML element
		 */
		protected String name;

		/**
		 * Map of attributes
		 */
		protected Map attributes = new HashMap();

		/**
		 * Body content to insert between opening and closing tags
		 */
		protected String bodyContent;

		/**
		 * Creates new instance of HTML element
		 *
		 * @param name Name of element
		 */
		public HTMLElement(String name) {
			this(name, new HashMap());
		}

		/**
		 * Creates new instance of HTML element with predefined set of attributes
		 *
		 * @param name       Name of element
		 * @param attributes Set of attributes
		 */
		public HTMLElement(String name, Map attributes) {
			if ( name == null || name.trim().length() == 0 ) {
				throw new IllegalArgumentException("Name must be non-null and non-empty string");
			}
			this.name = name.trim();
			if ( attributes != null ) {
				this.attributes = attributes;
			}
		}

		/**
		 * Adds new attribute. If attribute with the same name already exists, replaces its value
		 *
		 * @param name  Name of attribute
		 * @param value Value of attribute
		 */
		public void putAttribute(String name, String value) {
			attributes.put(name, value);
		}

		/**
		 * Removes attribute with specified name
		 *
		 * @param name Name of attribute to remove
		 */
		public void removeAttribute(String name) {
			attributes.remove(name);
		}

		/**
		 * Sets body content
		 *
		 * @param bodyContent Body content to set
		 */
		public void setBodyContent(String bodyContent) {
			this.bodyContent = bodyContent;
		}

		/**
		 * Puts all attributes together, inserts body content and returns element, represented as string.
         * No escaping is made.
		 *
		 * @return String representation of this element
		 */
		public String toString() {
			return toString(false);
		}

		/**
		 * Puts all attributes together, inserts body content and returns element, represented as string.
		 * Escapes all values if requested
         *
		 * @param escapeValues Whether or not to escape values
		 * @return String representation of this element
		 */
		public String toString(boolean escapeValues) {
			StringBuffer element = new StringBuffer();
			element.append("<").append(name);

			// append attributes
			for ( Iterator i = attributes.entrySet().iterator(); i.hasNext(); ) {
				Map.Entry attribute = (Map.Entry) i.next();
				String name = (String) attribute.getKey();
				String value = (String) attribute.getValue();
				if ( escapeValues ) {
					value = StringUtil.javascriptEscape(value);
				}
				element.append(" ").append(name).append("=\"").append(value).append("\"");
			}

			element.append(">");

			// append body content if any
			if ( bodyContent != null ) {
				element.append(bodyContent);
			}

			// close tag
			element.append("</").append(name).append(">");

			return element.toString();
		}
	}
}
